/*
 * Copyright 2010 Google Inc.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */
package java.io;

import java.util.ArrayList;

import com.google.gwt.core.client.JavaScriptObject;

/**
 * LocalStorage based File implementation for GWT. Should probably have used Harmony as a
 * starting point instead of writing this from scratch.
 * 
 * @author Stefan Haustein
 */
public class File {

  public static final File ROOT = new File("");
	
  public static final char separatorChar = '/';

  public static final String separator = "" + separatorChar;

  public static final char pathSeparatorChar = ':';

  public static final String pathSeparator = "" + pathSeparatorChar;

  File parent;
  String name;
  boolean absolute;
  
  public File(String pathname) {
	  while (pathname.endsWith(separator) && pathname.length() > 0) {
		  pathname = pathname.substring(0, pathname.length() - 1);
	  }
	  
	  int cut = pathname.lastIndexOf(separatorChar);
	  if (cut == -1) {
		  name = pathname;
	  } else if (cut == 0) { 
		  name = pathname.substring(cut);
		  parent = name.equals("") ? null : ROOT;
	  } else {
		  name = pathname.substring(cut + 1);
		  parent = new File(pathname.substring(0, cut));
	  }
	  
//	  Compatibility.println("new File ('"+pathname+ "'); canonical name: '" + getCanonicalPath() + "'");
  }

  public File(String parent, String child) {
	  this(new File(parent), child);
  }

  public File(File parent, String child) {
	  this.parent = parent;
	  this.name = child;
  }

  /*
  public File(URI uri) {
  }
  */

  public String getName() {
    return name;
  }

  public String getParent() {
    return parent == null ? "" : parent.getPath();
  }

  public File getParentFile() {
    return parent;
  }

  public String getPath() {
    return parent == null ? name : (parent.getPath() + 
    		separatorChar + name);
  }

  private boolean isRoot() {
	  return name.equals("") && parent == null;
  }
  
  public boolean isAbsolute() {
	if (isRoot()) {
		return true;
	}
	if(parent == null) {
		return false;
	}
	return parent.isAbsolute();
  }

  public String getAbsolutePath() {
    String path = getAbsoluteFile().getPath();
    return path.length() == 0 ? "/" : path;
  }

  public File getAbsoluteFile() {
	 if (isAbsolute()) { 
		 return this;
	 }
	 if (parent == null) {
		 return new File(ROOT, name);
	 }
	 return new File(parent.getAbsoluteFile(), name);
  }

  public String getCanonicalPath() {
	 return getCanonicalFile().getAbsolutePath();
  }

  public File getCanonicalFile() {
	  File cParent = parent == null ? null : parent.getCanonicalFile();
	  if (name.equals (".")) {
		  return cParent == null ? ROOT : cParent;
	  }
	  if (cParent != null && cParent.name.equals("")) {
		  cParent = null;
	  }
	  if (name.equals("..")) {
		  if (cParent == null) {
			  return ROOT;
		  } 
		  if (cParent.parent == null) {
			  return ROOT;
		  }
		  return cParent.parent;
	  }
	  if (cParent == null && !name.equals("")) {
		  return new File(ROOT, name);
	  }
	  return new File(cParent, name);
  }

  /*
  public URL toURL() throws MalformedURLException {
  }

  public URI toURI() {
  }
  */

  public boolean canRead() {
    return true;
  }

  public boolean canWrite() {
    return true;
  }

  public boolean exists() {
	try {
		return LocalStorage.getItem(getCanonicalPath()) != null;
	} catch (IOException e) {
		return false;
	}
  }

  public boolean isDirectory() {
	  try {
		  String s = LocalStorage.getItem(getCanonicalPath());
		  return s != null && s.startsWith("{");
	  } catch (IOException e) {
		  return false;
	  }
  }

  public boolean isFile() {
	  try {
		  String s = LocalStorage.getItem(getCanonicalPath());
		  return s != null && !s.startsWith("{");
	  } catch (IOException e) {
		  return false;
	  }
  }

  public boolean isHidden() {
    return false;
  }

  public long lastModified() {
    return 0;
  }

  public long length() {
	try {
		if (!exists()) {
			return 0;
		}
		
		RandomAccessFile raf = new RandomAccessFile(this, "r");
		long len = raf.length();
		raf.close();
		return len;
	} catch (IOException e) {
		return 0;
	}
  }

  public boolean createNewFile() throws IOException {
	  if (exists()) return false;
	  if (!parent.exists()) return false;
	  LocalStorage.setItem(getCanonicalPath(), RandomAccessFile.btoa(""));
	  return true;
  }

  public boolean delete() {
	  try {
		  if (!exists()) {
			  return false;
		  }
		  LocalStorage.removeItem(getCanonicalPath());
		  return true;
	  } catch (IOException e) {
		  return false;
	  }
  }

  public void deleteOnExit() {
	  throw new RuntimeException("NYI: File.deleteOnExit()");
  }

  public String[] list() {
	  throw new RuntimeException("NYI: File.list()");
  }

  /*
  public String[] list(FilenameFilter filter) {
    return null;
  }
  */

  public File[] listFiles() {
	 return listFiles(null);
  }

  
  public File[] listFiles(FilenameFilter filter) {
	  ArrayList<File> files = new ArrayList<File>();
	  try {
		  String prefix = getCanonicalPath();
		  if (!prefix.endsWith(separator)) {
			  prefix += separatorChar;
		  }

		  int cut = prefix.length();
		  int cnt = LocalStorage.length();
		  for (int i = 0; i < cnt; i++) {
			  String key = LocalStorage.key(i);
			  if (key.startsWith(prefix) && key.indexOf(separatorChar, cut) == -1) {
				  String name = key.substring(cut);
				  if (filter == null || filter.accept(this, name)) {
					  files.add(new File(this, name));
				  }
			  }
		  }
	  } catch (IOException e) {
		  System.err.println("lisFiles() exception: " + e);
	  }
	  return files.toArray(new File[files.size()]);
  }
 
  /*
  public File[] listFiles(FileFilter filter) {
    return null;
  }
  */

  public boolean mkdir() { 
	  try {
		  if (parent != null && !parent.exists()) {
			  return false;
		  }
		  if (exists()) {
			  return false;
		  }
		  // We may want to make this a JS map
		  LocalStorage.setItem(getCanonicalPath(), "{}");
		  return true;
	  } catch (IOException e) {
		  return false;
	  }
  }

  public boolean mkdirs() {
	  if (parent != null) {
		  parent.mkdirs();
	  }
	  return mkdir();
  }

  public boolean renameTo(File dest) {
	  throw new RuntimeException("renameTo()");
  }

  public boolean setLastModified(long time) {
    return false;
  }

  public boolean setReadOnly() {
    return false;
  }

  public static File[] listRoots() {
    return new File[]{ROOT};
  }

  public static File createTempFile(String prefix, String suffix, File directory)
      throws IOException {
	  throw new RuntimeException("NYI: createTempFile");
  }

  public static File createTempFile(String prefix, String suffix)
      throws IOException {
	  throw new RuntimeException("NYI: createTempFile");
  }

  public int compareTo(File pathname) {
	  throw new RuntimeException("NYI: File.compareTo()");
  }

  public boolean equals(Object obj) {
    if (!(obj instanceof File)) {
    	return false;
    }
    return getPath().equals(((File) obj).getPath());
  }

  public int hashCode() {
	  return parent != null ? parent.hashCode() + name.hashCode() :
		  name.hashCode();
  }

  public String toString() {
    return name;
  }
}
